package 方法句柄;

//public MethodHandle findSpecial​(类<?> refc, String name, MethodType type, 类<?> specialCaller) 
//		throws NoSuchMethodException, IllegalAccessException
//
//为虚方法生成早期绑定方法句柄。 它会绕过检查重写在接收机，方法as if called从invokespecial从明确内指令指定specialCaller 。 方法句柄的类型将是方法的
//类型，具有适当受限的接收器类型。 （接收器类型将为specialCaller或子类型。）查找对象必须可以访问该方法及其所有参数类型。
//在方法解析之前，如果显式指定的调用者类与查找类不相同，或者此查找对象没有private access特权，则访问将失败。
//
//当且仅当设置了方法的变量arity修饰符位（ 0x0080 ）时，返回的方法句柄将具有variable arity 。
//
//（注意：此API不显示名为"<init>" JVM内部方法，即使invokespecial指令在特殊情况下可以引用它们。使用findConstructor以安全的方式访问实例初始化方法。）
//
//例：
import static org.junit.Assert.assertEquals;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.util.ArrayList;

import org.junit.jupiter.api.Test;

//例：
import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;

/*public class findSpecial用法 {
	static class Listie extends ArrayList {
		public String toString() {
			return "[wee Listie]"; 
		}
		static Lookup lookup() {
			return MethodHandles.lookup(); 
		}
	}
	public static void main(String[] args) throws Throwable {
		MethodType mt1=MethodType.methodType(void.class);
		MethodHandle MH_newListie = Listie.lookup().findConstructor(Listie.class, mt1);//获得构造器方法句柄
		Listie l = (Listie) MH_newListie.invokeExact();//通过获得的构造器实例化Listie 相当于：Listie l=new Listie()
		Listie ll=new Listie();
		System.out.println(l);//[wee Listie]
		System.out.println(ll);//[wee Listie]
		
		//不能调用类内部方法<init>实例化类：
		try {
			assertEquals("impossible", Listie.lookup().findSpecial(Listie.class, "<init>", MethodType.methodType(void.class), Listie.class));
		} catch (NoSuchMethodException ex) {
			 System.out.println("impossible");
		} // OK
		
		MethodType mt2=MethodType.methodType(String.class);
		MethodHandle MH_super = Listie.lookup().findSpecial(ArrayList.class, "toString" , mt2, Listie.class);
		MethodHandle MH_this = Listie.lookup().findSpecial(Listie.class, "toString" , mt2, Listie.class);
		MethodHandle MH_duper = Listie.lookup().findSpecial(Object.class, "toString" , mt2, Listie.class);
		assertEquals("[]", (String) MH_super.invokeExact(l));//调用父类toString方法
		assertEquals(""+l, (String) MH_this.invokeExact(l));//调用this.toString方法
		assertEquals("[]", (String) MH_duper.invokeExact(l)); //调用Object.toString方法 ArrayList method
		
		try { 
			assertEquals("inaccessible", Listie.lookup().findSpecial(String.class, "toString", mt2, Listie.class));
		 } catch (IllegalAccessException ex) { } // OK
		Listie subl = new Listie() {
			public String toString() {
				return "[subclass]";
				}
			};
		assertEquals(""+l, (String) MH_this.invokeExact(subl)); // Listie method
	}
}*/
/*
参数:
refc - 从中访问方法的类或接口
name - 方法的名称（不能是“<init>”）
type - 方法的类型，省略receiver参数
specialCaller - 建议的调用类执行 invokespecial

结果:所需的方法句柄

异常:
NoSuchMethodException - 如果该方法不存在
IllegalAccessException - 如果访问检查失败，或者方法是 static ，或者方法的变量arity修饰符位已设置且 asVarargsCollector失败
SecurityException - 如果存在安全管理器且 refuses access
NullPointerException - 如果任何参数为null
例：
*/

//自定义MethodHandle.Lookup  访问private方法
/*class GrandFather {

	protected void thinking() {
		System.out.println("I 'm grandFather!");
	}

	private void hello() {
		System.out.println("grandFather say hello !!!");
	}
}

class Father extends GrandFather {

	@Override
	protected void thinking() {
		System.out.println("I 'm father!");
	}

	private void hello() {
		System.out.println("father say hello !!!");
	}
}

class Son extends Father {

	@Override
	protected void thinking() {
		System.out.println("I 'm Son!");
	}
}

class mhUtil {
//	//通过反射获取MethodHandles类中privateLookupIn方法句柄
//	private static Method privateLookupInMethod;
//	static {
//		//public static Lookup privateLookupIn(Class<?> targetClass, Lookup caller)
//		try {
//			privateLookupInMethod=MethodHandles.class.getMethod("privateLookupIn", Class.class,MethodHandles.Lookup.class);
//			//                                                            Class<?> targetClass,Lookup caller
//			System.out.println(privateLookupInMethod.toString());
//		} catch (NoSuchMethodException e){
//			e.printStackTrace();
//		}
//	}
//	//再通过privateLookupIn方法句柄，构造MethodHandles.Lookup，参数数为Callerclass,即：私有方法拥有者
//	public static MethodHandles.Lookup lookup(Class<?> Callerclass){
//		try {
//			MethodHandles.Lookup mhs=(MethodHandles.Lookup)privateLookupInMethod.invoke(MethodHandles.class,Callerclass, MethodHandles.lookup());
//			//System.out.println(mhs.toString());
//			return mhs;
//		}catch(IllegalAccessException | InvocationTargetException e) {
//			throw new RuntimeException(e);
//		}
//	}

	// 直接使用privateLookupIn方法 构造MethodHandles.Lookup，参数数为Callerclass,即：私有方法拥有者
	public static MethodHandles.Lookup lookup(Class<?> Callerclass) {
		try {
			return MethodHandles.privateLookupIn(Callerclass, MethodHandles.lookup());

		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		}
	}
}

//public MethodHandle findSpecial(Class<?> refc, String name, MethodType type,Class<?> specialCaller)
public class findSpecial用法 {
	public static void main(String[] args) throws Throwable {
		Son son = new Son();
		son.thinking();

		MethodHandles.Lookup mhl1 = mhUtil.lookup(Father.class);//
		System.out.println(mhl1.toString());// 方法句柄.Father
		MethodType mt1 = MethodType.methodType(void.class);
		System.out.println(mt1.toString());// ()void
		MethodHandle mh1 = mhl1.findSpecial(Father.class, "thinking", mt1, Father.class);
		mh1.invoke(son);// I 'm father!
		MethodHandle mh11 = mhl1.findSpecial(GrandFather.class, "thinking", mt1, Father.class);
		mh11.invoke(son);// I 'm grandFather!

		MethodHandle mh12 = mhl1.findSpecial(Father.class, "hello", mt1, Father.class);
		mh12.invoke(son);// father say hello !!!

		MethodHandles.Lookup mhl2 = mhUtil.lookup(GrandFather.class);
		System.out.println(mhl2.toString());// 方法句柄.GrandFather
		MethodType mt2 = MethodType.methodType(void.class);
		MethodHandle mh2 = mhl2.findSpecial(GrandFather.class, "thinking", mt2, GrandFather.class);
		mh2.invoke(son);// I 'm grandFather!

		// 反射调用
		Method hello1 = Father.class.getDeclaredMethod("hello");
		hello1.setAccessible(true);
		hello1.invoke(son);// father say hello !!!

		// 调用父类中的私有方法,因为子类没法重写父类的私有方法,所以调用结果跟反射一样.
		MethodHandles.Lookup lookup1 = mhUtil.lookup(Father.class);
		MethodHandle methodHandle1 = lookup1.unreflectSpecial(hello1, Father.class);
		methodHandle1.invoke(son);// father say hello !!!

		// 调用祖父类中的私有方法
		MethodHandles.Lookup lookup2 = mhUtil.lookup(GrandFather.class);
		Method hello2 = GrandFather.class.getDeclaredMethod("hello");
//      hello2.setAccessible(false);
		MethodHandle methodHandle2 = lookup2.unreflectSpecial(hello2, GrandFather.class);
		methodHandle2.invoke(son);// grandFather say hello !!!
	}
}*/

//访问私有方法
//public class OtherClass { 
//    private void money(){ 
//      System.out.println("money");  
//    }
//}
/*import 方法句柄.MethodHandle类.OtherClass;
class Util{
	public static MethodHandles.Lookup lookup(Class<?> CallerClass) throws Throwable{
		//public static Lookup privateLookupIn(Class<?> targetClass, Lookup caller)
		return (MethodHandles.Lookup)MethodHandles.privateLookupIn(CallerClass, MethodHandles.lookup());
	}
}
public class findSpecial用法 { 
    public static void main(String[] args) throws Throwable {

    	//1.先构造私有方法访问MethodHandles.Lookup
    	MethodHandles.Lookup lp = Util.lookup(OtherClass.class);    
    	OtherClass otherClass = (OtherClass) new OtherClass();    
    	MethodHandle methodHandle = lp.findSpecial(OtherClass.class, "money", MethodType.methodType(void.class),OtherClass.class);
    	methodHandle.invoke(otherClass);

    	//2.直接使用MethodHandles.privateLookupIn
    	MethodHandles.Lookup lp0=MethodHandles.privateLookupIn(OtherClass.class,MethodHandles.lookup());
    	MethodHandle methodHandle0 = lp0.findSpecial(OtherClass.class, "money", MethodType.methodType(void.class),OtherClass.class);
    	methodHandle0.invoke(otherClass);
    }
}*/

